////////////////////////////////////////////////////////
// CodePlex - metacpp
//--------------------
// This file is part of "metacpp" project on CodePlex. The
// goal if the project is to demonstrate basic techniques
// used in C++ metaprogrammins.
// For more info and comments please visit:
// http://www.codeplex.com/metacpp/
////////////////////////////////////////////////////////

#pragma once;

#include "condition.hpp"


namespace metacpp {

/// Used as "null" type in type lists
class nulltype {};

/// Used as "0" value returned by type algorithms
class emptytype {}; 


/**
 * The list of is represented in following form:
 * (T1, (T2, (T3, empty)))
 */
template <typename HEAD, typename TAIL>
struct typelist {
	typedef HEAD head;
	typedef TAIL tail;
};

/// macros for typelist initialization
#define TYPELIST_1(T1)					typelist<T1, nulltype>
#define TYPELIST_2(T1, T2)				typelist<T1, TYPELIST_1(T2)>
#define TYPELIST_3(T1, T2, T3)			typelist<T1, TYPELIST_2(T2, T3)>
#define TYPELIST_4(T1, T2, T3, T4)		typelist<T1, TYPELIST_3(T2, T3, T4)>
#define TYPELIST_5(T1, T2, T3, T4, T5)	typelist<T1, TYPELIST_4(T2, T3, T4, T5)>


/**
 * Recursive calculation of the length of
 * given typelist
 */
template <typename> struct tlist_length;

template <typename HEAD, typename TAIL>
struct tlist_length< typelist<HEAD, TAIL> > {
	enum {
		value = 1 + tlist_length<TAIL>::value
	};
};

template <> 
struct tlist_length< nulltype > {
	enum { value = 0 };
};


/**
 * Extraction of a type at specific position
 */
template <typename TLIST, int POS> struct tlist_get_type;

template <typename HEAD, typename TAIL, int POS>
struct tlist_get_type< typelist<HEAD, TAIL>, POS > {
	typedef typename tlist_get_type<TAIL, POS-1>::type type;
};
template <typename T, typename TAIL> 
struct tlist_get_type< typelist<T, TAIL>, 0 > {
	typedef T type;
};
template <int POS> 
struct tlist_get_type< nulltype, POS > {
	// no typedef - compilation error
};


/**
 * Find the largest type (in terms of "sizeof")
 */
template <typename TLIST> struct largest_type;

template <typename HEAD, typename TAIL>
struct largest_type< typelist<HEAD, TAIL> > {
	typedef typename largest_type<TAIL>::type _largest_tail;
	typedef typename type_if<sizeof(HEAD) >= sizeof(_largest_tail),
		HEAD, _largest_tail>::type type;
};

template <typename T> 
struct largest_type< typelist<T, nulltype> > {
	typedef T type;
};


/**
 * Find index of a type
 */
template <typename TLIST, typename T> struct index_of_type;

template <typename HEAD, typename TAIL, typename T>
struct index_of_type< typelist<HEAD, TAIL>, T> {
	enum {
		value = 1 + index_of_type<TAIL, T>::value
	};
};
template <typename T, typename TAIL> 
struct index_of_type< typelist<T, TAIL>, T > {
	enum { value = 0 };
};

template <typename NOTFOUND> 
struct index_of_type< nulltype, NOTFOUND > {
	// no value defined
};


/**
 * Apply a template for each type in the list,
 * and return it as a new typelist
 */
template <typename TLIST, template <typename> class WRAPPER>
struct apply_for_each;

template <typename HEAD, typename TAIL, template <typename> class WRAPPER>
struct apply_for_each< typelist<HEAD, TAIL>, WRAPPER > {
	typedef typelist<WRAPPER<HEAD>, typename apply_for_each<TAIL, WRAPPER>::type> type;
};

template <template <typename> class WRAPPER >
struct apply_for_each< nulltype, WRAPPER > {
	typedef nulltype type;
};






/**
 * Creates "visitor" interface - which will
 * require implementing operator() for each
 * of the types.
 *
 * For list (int, double, std::string) we would like to
 * generate an equivalent of:
 * class AbstractVisitor {
 *    virtual void operator()(const int& i) = 0;
 *    virtual void operator()(const double& i) = 0;
 *    virtual void operator()(const std::string& i) = 0;
 * };
 *
 * The result is achieved by generating hierarchy
 * of classes.
 */
template <typename LIST> struct AbstractVisitor;

template <typename HEAD, typename TAIL>
struct AbstractVisitor< typelist<HEAD, TAIL> > 
	: public AbstractVisitor<TAIL>
{
	virtual void operator()(const HEAD&) = 0;
};

template <>
struct AbstractVisitor<nulltype> {};


}  // end namespace